/*
 * Copyright (c) 2011 Auzingers
 * Created by Oui-IT
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.auzingers.docsorganizer.googleapi.xml;

import com.auzingers.docsorganizer.DocsOrganizerApplication;
import com.auzingers.docsorganizer.entity.GoogleAuthor;
import com.auzingers.docsorganizer.entity.GoogleCollection;
import com.auzingers.docsorganizer.entity.GoogleDocument;
import com.auzingers.docsorganizer.entity.GoogleEntry;
import com.auzingers.docsorganizer.entity.GoogleResult;
import com.auzingers.docsorganizer.util.StringUtil;

import org.xml.sax.Attributes;

import android.sax.Element;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.sax.StartElementListener;
import android.util.Xml;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Utility class for parsing xml returned from google. Note: This class is not thread-safe, it you need to use
 * it multiple times, you need to create multiple instances.
 */
public final class XmlParser {

    static final String TAG = DocsOrganizerApplication.APP_TAG + ".XmlParser";

    private static final String NAMESPACE = "http://www.w3.org/2005/Atom";

    private static final String NAMESPACE_GD = "http://schemas.google.com/g/2005";

    //cache member variables until we know what kind of object it is
    private String elementId;

    private String elementTitle;

    private String elementResourceId;

    private String elementAuthorName;

    private String elementAuthorEmail;

    private GoogleAuthor elementAuthor;

    private String elementFeedAuthorName;

    private String elementFeedAuthorEmail;

    private String elementCategoryKind;

    private List<String> elementParentRefs = new LinkedList<String>();

    /**
     * Internal method that parses an xml string that contains a list of google entries returned from
     * Google and converts them into a list of GoogleEntry objects (used for the common attributes of
     * a collection and a document).
     *
     * @param xml xml to parse
     * @return list of GoogleEntry objects filled with data from the xml
     */
    public GoogleResult parseGoogleEntries(final String xml) {
        final GoogleResult result = new GoogleResult();
        final List<GoogleEntry> entries = new ArrayList<GoogleEntry>();
        final RootElement feed = new RootElement(NAMESPACE, "feed");
        final Element feedLink = feed.getChild(NAMESPACE, "link");
        final Element feedAuthor = feed.getChild(NAMESPACE, "author");
        final Element feedAuthorName = feedAuthor.getChild(NAMESPACE, "name");
        final Element feedAuthorEmail = feedAuthor.getChild(NAMESPACE, "email");
        final Element entry = feed.getChild(NAMESPACE, "entry");
        final Element entryTitle = entry.getChild(NAMESPACE, "title");
        final Element entryCategory = entry.getChild(NAMESPACE, "category");
        final Element link = entry.getChild(NAMESPACE, "link");
        final Element id = entry.getChild(NAMESPACE, "id");
        final Element resourceId = entry.getChild(NAMESPACE_GD, "resourceId");
        final Element author = entry.getChild(NAMESPACE, "author");
        final Element authorName = author.getChild(NAMESPACE, "name");
        final Element authorEmail = author.getChild(NAMESPACE, "email");
        entry.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                elementId = null;
                elementTitle = null;
                elementResourceId = null;
                elementParentRefs = new LinkedList<String>();
                elementAuthor = null;
                elementAuthorName = null;
                elementAuthorEmail = null;
                elementCategoryKind = null;
            }
        });
        entry.setEndElementListener(new EndElementListener() {
            @SuppressWarnings("unchecked")
            public void end() {
                if (elementId != null) {
                    GoogleEntry element = null;
                    if (elementId.contains("folder")) {
                        element = new GoogleCollection();
                    } else {
                        element = new GoogleDocument();
                    }
                    element.setId(elementId);
                    element.setTitle(elementTitle);
                    element.setResourceId(elementResourceId);
                    element.setParentRefs(elementParentRefs);
                    element.setAuthor(elementAuthor);
                    element.setCategoryKind(elementCategoryKind);
                    entries.add(element);
                }
            }
        });
        feed.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                elementFeedAuthorEmail = null;
                elementFeedAuthorName = null;
            }
        });
        feedLink.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                final String rel = attributes.getValue("rel");
                if ("next".equalsIgnoreCase(rel)) {
                    final String href = attributes.getValue("href");
                    final String strStartKey = "start-key=";
                    final int index = href.indexOf(strStartKey);
                    if (index >= 0) {
                        String startKey = href.substring(index + strStartKey.length());
                        final int idx = startKey.indexOf("&");
                        if (idx > 0) {
                            startKey = startKey.substring(0, idx);
                        }
                        result.setNextStartKey(startKey);
                    }
                }
            }
        });
        feedAuthor.setEndElementListener(new EndElementListener() {
            public void end() {
                if (elementFeedAuthorName != null && elementFeedAuthorEmail != null) {
                    result.setFeedAuthor(new GoogleAuthor(elementFeedAuthorName, elementFeedAuthorEmail));
                }
            }
        });
        feedAuthorName.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementFeedAuthorName = StringUtil.trim(body);
            }
        });
        feedAuthorEmail.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementFeedAuthorEmail = StringUtil.trim(body);
            }
        });
        entryCategory.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                final String scheme = attributes.getValue("scheme");
                if (scheme.endsWith("#kind")) {
                    String term = attributes.getValue("term");
                    if (term != null) {
                        final int idx = term.indexOf("#");
                        if (idx > 0) {
                            term = term.substring(idx + 1);
                        }
                        elementCategoryKind = term;
                    }
                }
            }
        });
        entryTitle.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementTitle = StringUtil.trim(body);
            }
        });
        id.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementId = StringUtil.trim(body);
            }
        });
        resourceId.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementResourceId = StringUtil.trim(body);
            }
        });
        link.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                final String href = attributes.getValue("href");
                final String rel = attributes.getValue("rel");
                if (rel != null && rel.contains("#parent")) {
                    elementParentRefs.add(StringUtil.trim(href));
                }
            }
        });
        authorEmail.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementAuthorEmail = StringUtil.trim(body);
            }
        });
        authorName.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                elementAuthorName = StringUtil.trim(body);
            }
        });
        author.setEndElementListener(new EndElementListener() {
            public void end() {
                if (elementAuthorName != null && elementAuthorEmail != null) {
                    elementAuthor = new GoogleAuthor(elementAuthorName, elementAuthorEmail);
                }
            }
        });

        try {
            Xml.parse(xml, feed.getContentHandler());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        result.setEntries(entries);
        return result;
    }
}
